package com.xiang.ad.mysql;

import com.alibaba.fastjson.JSON;
import com.xiang.ad.mysql.constant.OpType;
import com.xiang.ad.mysql.dto.ParseTemplate;
import com.xiang.ad.mysql.dto.TableTemplate;
import com.xiang.ad.mysql.dto.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;

/**
 * Created by xiang.
 * 解析模板文件，实现映射关系   列索引-列名的映射
 * binlog不能直接使用，
 *下面的查询结果，可以方便用于解析对应的名字和列
 *
 mysql> select table_schema,table_name,column_name,ordinal_position
 -> from information_schema.columns where table_schema = 'xiang_ad_data'
 -> and table_name = 'ad_unit_keyword';
 +---------------+-----------------+-------------+------------------+
 | TABLE_SCHEMA  | TABLE_NAME      | COLUMN_NAME | ORDINAL_POSITION |
 +---------------+-----------------+-------------+------------------+
 | xiang_ad_data | ad_unit_keyword | id          |                1 |
 | xiang_ad_data | ad_unit_keyword | unit_id     |                2 |
 | xiang_ad_data | ad_unit_keyword | keyword     |                3 |
 +---------------+-----------------+-------------+------------------+
 3 rows in set (0.02 sec)
 */
@Slf4j
@Component
public class TemplateHolder {

    private ParseTemplate template;//引入解析类，用来解析template
    private final JdbcTemplate jdbcTemplate;//因为根据sql查询，需要使用JdbcTemplate

    //定义sql语句  查询每张表中的table_schema信息，实现映射
    private String SQL_SCHEMA = "select table_schema, table_name, " +
            "column_name, ordinal_position from information_schema.columns " +
            "where table_schema = ? and table_name = ?";

    @Autowired
    public TemplateHolder(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }


    //loadJson方法应该在容器启动的时候就应该执行
    //也就是当TemplateHolder被SrpingIOC容器加载的时候就该执行了
    @PostConstruct
    private void init() {
        loadJson("template.json");
    }

    //对外服务的方法，对外提供TableTemplate的获取方式 通过表的名字可以拿到这个表的所有信息
    public TableTemplate getTable(String tableName) {
        return template.getTableTemplateMap().get(tableName);
    }

    //加载并解析配置template.json文件
    private void loadJson(String path) {

        ClassLoader cl = Thread.currentThread().getContextClassLoader();//获取类加载器
        InputStream inStream = cl.getResourceAsStream(path);//获取输入流

        try {
            //模板文件是json格式的
            Template template = JSON.parseObject(
                    inStream,//输入流
                    Charset.defaultCharset(),//字符集 选默认的
                    Template.class //准备好的模板文件
            );
            this.template = ParseTemplate.parse(template);//解析template，对模板文件进行解析
            loadMeta();//通过每张表的schema信息查询  实现索引到列名的映射
        } catch (IOException ex) {
            log.error(ex.getMessage());
            throw new RuntimeException("fail to parse json file");
        }
    }

    //通过每张表的schema信息查询  实现索引到列名的映射
    private void loadMeta() {

        //从对每张表循环  key是表的名字，value是table的模板
        for (Map.Entry<String, TableTemplate> entry :
                template.getTableTemplateMap().entrySet()) {//对各个table进行遍历

            //获取table的定义
            TableTemplate table = entry.getValue();

            //更新字段
            List<String> updateFields = table.getOpTypeFieldSetMap().get(
                    OpType.UPDATE
            );
            //插入字段
            List<String> insertFields = table.getOpTypeFieldSetMap().get(
                    OpType.ADD
            );
            //删除字段
            List<String> deleteFields = table.getOpTypeFieldSetMap().get(
                    OpType.DELETE
            );

            //使用jdbcTemplate实现查询
            jdbcTemplate.query(SQL_SCHEMA, new Object[]{
                    //数据库名称，表名称
                    template.getDatabase(), table.getTableName()
            }, (rs, i) -> {
                //获取并填充映射关系
                int pos = rs.getInt("ORDINAL_POSITION");//
                String colName = rs.getString("COLUMN_NAME");//
                //对应进行填充
                if ((null != updateFields && updateFields.contains(colName))
                        || (null != insertFields && insertFields.contains(colName))
                        || (null != deleteFields && deleteFields.contains(colName))) {
                    table.getPosMap().put(pos - 1, colName);//-1是因为对应的索引位置关系
                }

                return null;
            });
        }
    }
}
